package com.mxin.network.factory

import androidx.lifecycle.LiveData
import retrofit2.Call
import retrofit2.CallAdapter
import retrofit2.Callback
import retrofit2.Response
import java.lang.reflect.*
import java.lang.reflect.Array
import java.util.*
import java.util.concurrent.atomic.AtomicBoolean

/**
 *  Created by ：2020/7/29
 *  author : Administrator
 *
 */
class LiveDataCallAdapter<R>(private val responseType:Type,
                             private val logicListener:LiveDataCallAdapterLogicListener<R>
    ):CallAdapter<R, LiveData<R>> {

    val type  = getRawType(responseType)

    override fun adapt(call: Call<R>): LiveData<R> {
        return object : LiveData<R>(){
            private var started = AtomicBoolean(false)
            override fun onActive() {
                super.onActive()
                if (started.compareAndSet(false, true)) {
                    call.enqueue(object : Callback<R> {
                        override fun onResponse(call: Call<R>, response: Response<R>) {
                            postValue(logicListener.adapterResponse(type, response.body(), response.code(), response.message()))
                        }

                        override fun onFailure(call: Call<R>, throwable: Throwable) {
                            postValue(logicListener.adapterFailure(type, throwable))
                        }
                    })
                }
            }
        }
    }

    override fun responseType(): Type = responseType


    private fun getRawType(type: Type): Class<*> {
        Objects.requireNonNull(type, "type == null")
        if (type is Class<*>) {
            // Type is a normal class.
            return type
        }
        if (type is ParameterizedType) {

            // I'm not exactly sure why getRawType() returns Type instead of Class. Neal isn't either but
            // suspects some pathological case related to nested classes exists.
            val rawType = type.rawType
            require(rawType is Class<*>)
            return rawType
        }
        if (type is GenericArrayType) {
            val componentType =
                type.genericComponentType
            return Array.newInstance(getRawType(componentType), 0).javaClass
        }
        if (type is TypeVariable<*>) {
            // We could use the variable's bounds, but that won't work if there are multiple. Having a raw
            // type that's more general than necessary is okay.
            return Any::class.java
        }
        if (type is WildcardType) {
            return getRawType(type.upperBounds[0])
        }
        throw IllegalArgumentException(
            "Expected a Class, ParameterizedType, or "
                    + "GenericArrayType, but <" + type + "> is of type " + type.javaClass.name
        )
    }
}